package com.doctcloud.datacloud.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doctcloud.common.core.domain.R;
import com.doctcloud.common.core.utils.bean.BeanUtils;
import com.doctcloud.datacloud.entity.PatMasterIndexEntity;
import com.doctcloud.datacloud.mapper.PatMasterIndexEntityMapper;
import com.doctcloud.hisinfo.api.RemoteHisInpAdmService;
import com.doctcloud.hisinfo.api.RemoteHisMedrecService;
import com.doctcloud.hisinfo.histable.domain.medrec.PatMasterIndexDo;
import com.doctcloud.hisinfo.histable.domain.inpadm.PatsInHospitalDo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PatMasterIndexEntityService extends ServiceImpl<PatMasterIndexEntityMapper, PatMasterIndexEntity> {

    private final RemoteHisInpAdmService remoteHisInpAdmService;
    private final RemoteHisMedrecService remoteHisMedrecService;

    @Value("${sync.batch.size:1000}")
    private int BATCH_SIZE;

    @Value("${sync.remote.query.batch.size:100}")
    private int REMOTE_QUERY_BATCH_SIZE;

    /**
     * 同步在院患者信息。
     * 此方法会从远程服务获取最新的在院患者列表，并将本地数据库中不存在的患者信息插入，已存在的则更新部分字段。
     * 采用分批循环调用单条查询接口的方式获取患者详细信息。
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncPatsInHospital() {
        log.info("--- 开始同步在院患者信息 ---");
        long startTime = System.currentTimeMillis();

        try {
            // 步骤1: 从远程服务获取所有在院患者的基础信息列表
            log.info("正在从远程服务获取在院患者列表...");
            R<List<PatsInHospitalDo>> remotePatientListR = remoteHisInpAdmService.listpatsinhospital();
            if (remotePatientListR == null || !R.isSuccess(remotePatientListR) || CollectionUtils.isEmpty(remotePatientListR.getData())) {
                String errorMsg = (remotePatientListR != null) ? remotePatientListR.getMsg() : "远程服务响应为null";
                log.error("从远程服务获取在院患者列表失败或结果为空：{}", errorMsg);
                return;
            }

            List<PatsInHospitalDo> remotePatientList = remotePatientListR.getData();
            log.info("从远程服务获取到 {} 条在院患者记录。", remotePatientList.size());

            // 提取并过滤患者ID
            List<String> allPatientIds = remotePatientList.stream()
                    .map(PatsInHospitalDo::getPatientId)
                    .filter(Objects::nonNull)
                    .filter(id -> !id.trim().isEmpty())
                    .distinct()
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(allPatientIds)) {
                log.info("没有需要同步的有效患者ID。");
                return;
            }
            log.info("共涉及 {} 个不重复的患者ID。", allPatientIds.size());

            // 步骤2: 批量查询本地数据库，找出已存在的患者
            log.info("正在查询本地数据库中已存在的患者记录...");
            LambdaQueryWrapper<PatMasterIndexEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.in(PatMasterIndexEntity::getPatientId, allPatientIds);
            List<PatMasterIndexEntity> existingPatients = baseMapper.selectList(queryWrapper);
            log.info("本地数据库中已存在 {} 条患者记录。", existingPatients.size());

            Map<String, PatMasterIndexEntity> existingPatientMap = existingPatients.stream()
                    .collect(Collectors.toMap(PatMasterIndexEntity::getPatientId, p -> p));

            // 创建 visitId 映射
            Map<String, PatsInHospitalDo> patientVisitMap = remotePatientList.stream()
                    .filter(p -> p.getPatientId() != null && !p.getPatientId().trim().isEmpty())
                    .collect(Collectors.toMap(PatsInHospitalDo::getPatientId, p -> p, (p1, p2) -> p1)); // 处理可能的ID重复

            // 步骤3: 分离出需要从远程获取详细信息的患者ID
            List<String> patientIdsToFetch = allPatientIds.stream()
                    .filter(patientId -> !existingPatientMap.containsKey(patientId))
                    .collect(Collectors.toList());

            log.info("本地缺少 {} 个患者的详细信息，将从远程分批获取...", patientIdsToFetch.size());

            // 步骤4: 分批从远程获取患者详细信息并处理（插入）
            Date currentDate = new Date();
            List<PatMasterIndexEntity> bufferForInsert = new ArrayList<>();

            for (int i = 0; i < patientIdsToFetch.size(); i += REMOTE_QUERY_BATCH_SIZE) {
                int end = Math.min(i + REMOTE_QUERY_BATCH_SIZE, patientIdsToFetch.size());
                List<String> batchIdsToFetch = patientIdsToFetch.subList(i, end);

                log.debug("正在处理第 {} 批患者详细信息查询 (单条接口)，批次大小: {}", (i / REMOTE_QUERY_BATCH_SIZE) + 1, batchIdsToFetch.size());

                for (String patientId : batchIdsToFetch) {
                    PatMasterIndexDo patMasterIndexDo = null;
                    try {
                        R<PatMasterIndexDo> patMasterIndexDoR = remoteHisMedrecService.getPatMasterIndexById(patientId);
                        if (patMasterIndexDoR != null && R.isSuccess(patMasterIndexDoR)) {
                            patMasterIndexDo = patMasterIndexDoR.getData();
                        } else {
                            String errorMsg = (patMasterIndexDoR != null) ? patMasterIndexDoR.getMsg() : "响应为空或失败";
                            log.warn("获取患者 [{}] 详细信息失败: {}", patientId, errorMsg);
                        }
                    } catch (Exception e) {
                        log.error("获取患者 [{}] 详细信息时发生异常，将跳过该患者。", patientId, e);
                    }

                    if (patMasterIndexDo == null || patMasterIndexDo.getPatientId() == null || patMasterIndexDo.getPatientId().trim().isEmpty()) {
                        log.warn("患者 [{}] 的详细信息为空或patientId无效，已跳过。", patientId);
                        continue;
                    }

                    PatMasterIndexEntity newEntity = new PatMasterIndexEntity();
                    BeanUtils.copyProperties(patMasterIndexDo, newEntity);

                    // 补充或覆盖从 PatsInHospitalDo 中获取的信息
                    PatsInHospitalDo inHospitalDo = patientVisitMap.get(patMasterIndexDo.getPatientId());
                    if (inHospitalDo != null) {
                        newEntity.setVisitId(inHospitalDo.getVisitId());
                        newEntity.setUpdateDate(new Date());
                    }

                    bufferForInsert.add(newEntity);

                    // 当缓冲区满时，执行批量插入
                    if (bufferForInsert.size() >= BATCH_SIZE) {
                        log.debug("患者信息插入缓冲区已满，执行批量插入 {} 条记录。", bufferForInsert.size());
                        baseMapper.batchInsert(bufferForInsert);
                        bufferForInsert.clear();
                    }
                }
            }

            // 处理缓冲区中剩余的待插入数据
            if (!bufferForInsert.isEmpty()) {
                log.debug("同步结束，处理缓冲区中剩余的 {} 条患者记录（插入）。", bufferForInsert.size());
                baseMapper.batchInsert(bufferForInsert);
                bufferForInsert.clear();
            }

            // 步骤5: 处理需要更新的患者
            log.info("开始处理需要更新的患者记录...");
            List<PatMasterIndexEntity> bufferForUpdate = new ArrayList<>();

            for (Map.Entry<String, PatMasterIndexEntity> entry : existingPatientMap.entrySet()) {
                String patientId = entry.getKey();
                PatMasterIndexEntity existingEntity = entry.getValue();
                PatsInHospitalDo inHospitalDo = patientVisitMap.get(patientId);

                // 根据业务需求，可以选择是否更新
                boolean needUpdate = true;
                /**
                 * 如果住院标识与本地住院标识相同，则无需更新。此处不需要，
                 */
//                if (inHospitalDo != null && !Objects.equals(existingEntity.getVisitId(), inHospitalDo.getVisitId())) {
//                    existingEntity.setVisitId(inHospitalDo.getVisitId());
//                    existingEntity.setUpdateDate(new Date());
//                    needUpdate = true;
//                }

                if (needUpdate) {
                    existingEntity.setUpdateDate(currentDate);
                    existingEntity.setVisitId(inHospitalDo.getVisitId());
                    bufferForUpdate.add(existingEntity);

                    // 当缓冲区满时，执行批量更新
                    if (bufferForUpdate.size() >= BATCH_SIZE) {
                        log.debug("患者信息更新缓冲区已满，执行批量更新 {} 条记录。", bufferForUpdate.size());
                        baseMapper.batchUpdate(bufferForUpdate);
                        bufferForUpdate.clear();
                    }
                }
            }

            // 处理缓冲区中剩余的待更新数据
            if (!bufferForUpdate.isEmpty()) {
                log.debug("同步结束，处理缓冲区中剩余的 {} 条患者记录（更新）。", bufferForUpdate.size());
                baseMapper.batchUpdate(bufferForUpdate);
                bufferForUpdate.clear();
            }

            log.info("在院患者信息同步任务全部完成。");

        } catch (Exception e) {
            log.error("同步在院患者信息时发生未知异常: ", e);
            throw new RuntimeException("同步在院患者信息失败，请检查日志并联系管理员。", e);
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("--- 在院患者信息同步任务结束，总耗时: {} ms ---", (endTime - startTime));
        }
    }
}